/*
 * Copyright 2011 Andrew Freer
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#define INITGUID
#include "resources.h"
#include "dxdiagcom.h"
#include "wine/unicode.h"
#include <wine/debug.h>
#include <assert.h>
#include <errno.h>
#include <initguid.h>
#include <dxdiag.h>
#include <oleauto.h>
#include <oaidl.h>
#include <stdio.h>
#include <wchar.h>

WINE_DEFAULT_DEBUG_CHANNEL(dxdiag);

typedef VOID *(*create_container) (IDxDiagContainer * iContainer);

static VOID *ParseContainer(IDxDiagContainer * iContainer,
                                const char *name, create_container create);
static VOID *GetSysInfo(IDxDiagContainer * iContainer);

HRESULT DxDiagCOM_init(IDxDiagContainer ** dxRoot)
{
    HRESULT hr;
    DXDIAG_INIT_PARAMS params;
    IDxDiagProvider *dxProvider = NULL;

    /* Initialize COM */
    hr = CoInitialize(NULL);
    if (FAILED(hr))
        return hr;

    /* Get the Dxdiag Provider */
    hr = CoCreateInstance(&CLSID_DxDiagProvider, NULL,
                          CLSCTX_INPROC_SERVER, &IID_IDxDiagProvider,
                          (LPVOID *) & dxProvider);
    if (FAILED(hr))
        return hr;

    /* Initialize the provider */
    params.dwSize = sizeof(params);
    params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION;
    params.bAllowWHQLChecks = TRUE;
    params.pReserved = NULL;
    hr = IDxDiagProvider_Initialize(dxProvider, &params);
    if (FAILED(hr)) {
        IDxDiagProvider_Release(dxProvider);
        return hr;
    }

    /* Get the root container */
    hr = IDxDiagProvider_GetRootContainer(dxProvider, dxRoot);
    IDxDiagProvider_Release(dxProvider);
    return hr;
}

BOOL DxDiagCOM_ParseRoot(IDxDiagContainer * iContainer, DxDiag_Root * root)
{
    root->systemInfo = ParseContainer(iContainer, "DxDiag_SystemInfo", GetSysInfo);
    if (!root->systemInfo)
        return FALSE;
    return TRUE;
}

static HRESULT GetPropStr(IDxDiagContainer * container,
                                      const char *property, WCHAR ** value)
{
    HRESULT hr;
    VARIANT var;
    INT length;
    assert(container);
    VariantInit(&var);
    length = MultiByteToWideChar(0, 0, property, -1, NULL, 0)+1;
    {
        WCHAR *propertyW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * length);
        if (!propertyW)
            return E_FAIL;

        MultiByteToWideChar(0, 0, property, -1, propertyW, length);
        hr = IDxDiagContainer_GetProp(container, propertyW, &var);
        HeapFree(GetProcessHeap(), 0, propertyW);
        if (hr != S_OK) {
            *value = NULL;
            return hr;
        }
        VariantChangeType(&var, &var, VARIANT_ALPHABOOL, VT_BSTR);
        *value = V_BSTR(&var);
        return hr;
    }
}

static VOID *ParseContainer(IDxDiagContainer * iContainer,
                                const char *name, create_container create)
{
    HRESULT hr;
    IDxDiagContainer *child;
    INT length;

    assert(iContainer != NULL);
    assert(name != NULL);
    length = MultiByteToWideChar(0, 0, name, -1, NULL, 0)+1;
    {
        WCHAR *nameW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*length);
        if (!nameW)
            return NULL;
        MultiByteToWideChar(0, 0, name, -1, nameW, length);

        hr = IDxDiagContainer_GetChildContainer(iContainer, nameW, &child);

        HeapFree(GetProcessHeap(), 0, nameW);

        if (hr == S_OK) {
            return create(child);
        } else {
            WINE_TRACE("Container %s could not be found\n", wine_dbgstr_w(nameW));
            return NULL;
        }

    }
}

static VOID *GetSysInfo(IDxDiagContainer * iContainer)
{
    DxDiag_SystemInfo *sysinfo;
    sysinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DxDiag_SystemInfo));

    if (!sysinfo)
        return NULL;

    GetPropStr(iContainer, "szTimeEnglish", &sysinfo->szTimeEnglish);
    GetPropStr(iContainer, "szMachineNameLocalized", &sysinfo->szMachineNameLocalized);
    GetPropStr(iContainer, "szOSExLongEnglish", &sysinfo->szOSExLongEnglish);
    GetPropStr(iContainer, "szLanguagesEnglish", &sysinfo->szLanguagesEnglish);
    GetPropStr(iContainer, "szSystemManufacturerEnglish", &sysinfo->szSystemManufacturerEnglish);
    GetPropStr(iContainer, "szSystemModelEnglish", &sysinfo->szSystemModelEnglish);
    GetPropStr(iContainer, "szBIOSEnglish", &sysinfo->szBIOSEnglish);
    GetPropStr(iContainer, "szProcessorEnglish", &sysinfo->szProcessorEnglish);
    GetPropStr(iContainer, "szPhysicalMemoryEnglish", &sysinfo->szPhysicalMemoryEnglish);
    GetPropStr(iContainer, "szPageFileLocalized", &sysinfo->szPageFileLocalized);
    GetPropStr(iContainer, "szWindowsDir", &sysinfo->szWindowsDir);
    GetPropStr(iContainer, "szDirectXVersionLongEnglish", &sysinfo->szDirectXVersionLongEnglish);
    GetPropStr(iContainer, "szSetupParamEnglish", &sysinfo->szSetupParamEnglish);

    return sysinfo;
}
